module JumbleList (Board, search) where

import Data.List (find, nub)
import Data.Maybe (fromMaybe)

-- ----------------------------------------------------------------------------
--                                  T Y P E S
-- ----------------------------------------------------------------------------

type Pos    = (Int,Int) 
type Square = (Pos,Char)
type Board  = (Int,[Square])

-- ----------------------------------------------------------------------------
--                              A C C E S S O R S 
-- ----------------------------------------------------------------------------

boardWidth :: Board -> Int
boardWidth brd = fst brd

boardSquares :: Board -> [Square]
boardSquares brd = snd brd

squarePos :: Square -> Pos
squarePos sqr = fst sqr

squareChar :: Square -> Char
squareChar sqr = snd sqr

-- ----------------------------------------------------------------------------
--                        H E L P E R  F U N C T I O N S 
-- ----------------------------------------------------------------------------

-- Return a square at a given pos on a given board
getSquareFromPos :: Board -> Pos -> Maybe Square
getSquareFromPos brd pos = find (\p -> squarePos p == pos) (boardSquares brd)

-- Return a list of squares on a given board that neighbor a given pos
neighbors :: Board -> Pos -> [Square]
neighbors brd (x,y) = map (fromMaybe ((-1,-1), ' '))
                          (filter (\n -> n /= Nothing) 
                                  [getSquareFromPos brd (x-1,y),
                                   getSquareFromPos brd (x+1,y),
                                   getSquareFromPos brd (x,y-1),
                                   getSquareFromPos brd (x,y+1),
                                   getSquareFromPos brd (x-1,y-1), 
                                   getSquareFromPos brd (x-1,y+1),
                                   getSquareFromPos brd (x+1,y-1),
                                   getSquareFromPos brd (x+1,y+1)])

-- Search for a string from a given square on a given board
searchFromSquare :: Board -> String -> [Square] -> Square -> [Square]
searchFromSquare brd str path sqr
    | null str                     = path
    | (head str) == squareChar sqr = concat (nub (map (searchFromSquare brd 
                                                                        (tail str) 
                                                                        (path ++ [sqr])) 
                                               (neighbors brd (squarePos sqr))))
    | otherwise                    = []

-- ----------------------------------------------------------------------------
--                      E X P O R T E D  F U N C T I O N S 
-- ----------------------------------------------------------------------------

-- Search for a string starting at every square on a given board
search :: Board -> String -> [[Square]]
search brd str = filter (\xs -> not (null xs))
                        (map (searchFromSquare brd str []) (boardSquares brd))

